package znet

import (
	"fmt"
	"gitee.com/MrDaiM/zinx/utils"
	"gitee.com/MrDaiM/zinx/ziface"
	"strconv"
)

type MsgHandle struct {

	// Apis 存放每个MsgId 所对应的处理方法Map属性
	Apis map[uint32]ziface.IRouter
	// 业务工作Worker池的数量
	WorkerPoolSize uint32
	// Worker 负责取任务的消息队列
	TaskQueue []chan ziface.IRequest
}

// NewMsgHandle 构造函数
func NewMsgHandle() *MsgHandle {
	return &MsgHandle{
		Apis:           make(map[uint32]ziface.IRouter),
		WorkerPoolSize: utils.GlobalObject.WorkerPoolSize,
		// 一个worker 对应一个queue
		TaskQueue: make([]chan ziface.IRequest, utils.GlobalObject.WorkerPoolSize),
	}
}

// DoMsgHandler 马上以非阻塞方式处理消息
func (mh *MsgHandle) DoMsgHandler(request ziface.IRequest) {
	// 判断该请求的路由是否存在
	handler, ok := mh.Apis[request.GetMsgID()]
	if !ok {
		fmt.Println("api msgId = ", request.GetMsgID(), " is not found")
		return
	}

	// 如果该路由存在，执行对应的处理方法
	handler.PreHandle(request)
	handler.Handle(request)
	handler.PostHandle(request)

}

// AddRouter 为消息添加具体的处理逻辑
func (mh *MsgHandle) AddRouter(msgId uint32, router ziface.IRouter) {

	// 判断当前的msg绑定的API处理方法是否已经存在
	if _, ok := mh.Apis[msgId]; ok {
		str := "request api , msgId = " + strconv.Itoa(int(msgId))
		panic(any(str))
	}
	// 添加msg与api的绑定关系
	mh.Apis[msgId] = router
	fmt.Println("Add api msgId = ", msgId)

}

func (mh *MsgHandle) StartOneWorker(workerID int, taskQueue chan ziface.IRequest) {
	fmt.Println("Worker ID = ", workerID, " is started")
	// 不断的等待队列中的消息
	for {
		select {
		// 有消息则取出队列的Request，并执行绑定的业务方法
		case request := <-taskQueue:
			mh.DoMsgHandler(request)
		}
	}
}

// StartWorkerPool 启动一个Worker的工作流程
func (mh *MsgHandle) StartWorkerPool() {
	// 遍历需要启动Worker的数量，依次启动
	for i := 0; i < int(mh.WorkerPoolSize); i++ {
		// 给一个worker被启动
		// 给当前Worker对应的任务队列开启空间
		mh.TaskQueue[i] = make(chan ziface.IRequest, utils.GlobalObject.MaxWorkTaskLen)
		// 启动当前Worker， 阻塞的等待对应的任务队列是否有消息传递进来
		go mh.StartOneWorker(i, mh.TaskQueue[i])
	}

}

// SendMsgToTaskQueue 将消息交给TaskQueue,由worker进行处理
func (mh *MsgHandle) SendMsgToTaskQueue(request ziface.IRequest) {

	// 向任务队列中发送消息
	// mh.TaskQueue[i] 在队列切片中该不同请求该向哪一个队列中发送呢？

	// 根据ConnID来分配当前的连接应该有那个Worker负责处理
	// 轮询平均法则

	// 得到需要处理此连接的workerID
	workID := request.GetConnection().GetConnId() % mh.WorkerPoolSize
	fmt.Println("Add ConnId= ", request.GetConnection().GetConnId(), " request msgId = ", request.GetMsgID(), " to WorkId = ", workID)
	// 将请求消息发送给任务队列
	mh.TaskQueue[workID] <- request

}
